﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace CoolCode
{
    public static class LazyBuilder
    {
        public static LazyBuilder<T> Create<T>()
        {
            return new LazyBuilder<T>();
        }

        public static LazyBuilder<T> Create<T>(Expression<Func<T>> expr)
        {
            throw new NotImplementedException();
        }
    }

    public class LazyBuilder<T>
    {
        private static LazyProxy<T> s_lazyProxy = new LazyProxy<T>();
        private static Func<T> s_defaultCreator = null;

        static LazyBuilder()
        {
            if (s_lazyProxy.HasDefaultConstructor)
            {
                var lambdaExpr = Expression.Lambda<Func<T>>(Expression.New(s_lazyProxy.ProxyType));
                s_defaultCreator = lambdaExpr.Compile();
            }
        }

        public T Instance { get; private set; }

        public LazyBuilder()
        {
            if (s_defaultCreator == null)
            {
                string message = String.Format("No default constructor defined for type {0}.", typeof(T));
                throw new InvalidOperationException(message);
            }

            this.Instance = s_defaultCreator();
        }

        public void Setup<TProperty>(Expression<Func<T, TProperty>> expr, Func<TProperty> loader)
        {
            var property = (expr.Body as MemberExpression).Member as PropertyInfo;
            s_lazyProxy.SetLazyLoader(property, this.Instance, loader);
        }
    }
    /*Sample:
     
        public void LazyLoading()
        {
            var lazyBuilder = LazyBuilder.Create<LazyTypeTestClass>();
            var instance = lazyBuilder.Instance;

            int count = 0;
            instance.NonVirtualProperty = count;
            lazyBuilder.Setup(c => c.VirtualProperty, () => count);
            count = 10;

            Assert.Equal(0, instance.NonVirtualProperty);
            Assert.Equal(10, instance.VirtualProperty);

            count = 100;
            Assert.Equal(10, instance.VirtualProperty);
        }
     */
}
